/*
 * Copyright (c) 2019. Mikes. All rights reserved.
 *
 * You can contact me with sending email to bWlrZXNfMjAxMkAxNjMuY29t for more information!
 */

package com.climber.android.commonsdk.app.configuration

import android.app.Application
import android.content.Context
import androidx.fragment.app.FragmentManager
import com.google.gson.ExclusionStrategy
import com.google.gson.FieldAttributes
import com.google.gson.TypeAdapter
import com.google.gson.reflect.TypeToken
import com.google.gson.stream.JsonReader
import com.google.gson.stream.JsonWriter
import com.climber.android.commonsdk.app.AppConstants
import com.climber.android.commonsdk.app.AppEnv
import com.climber.android.commonsdk.api.interceptor.AuthorizationInterceptor
import com.climber.android.commonsdk.api.interceptor.MockAPIInterceptor
import com.climber.android.commonsdk.realm.RealmDouble
import com.climber.android.commonsdk.realm.RealmInt
import com.climber.android.commonsdk.realm.RealmString
import com.readystatesoftware.chuck.ChuckInterceptor
import io.ganguo.library.mvp.http.converter.APIResponseDataConverter
import io.ganguo.library.mvp.http.interceptor.RequestInterceptor
import io.ganguo.library.mvp.ui.mvp.base.delegate.AppLifecycles
import io.ganguo.library.mvp.ui.mvp.di.module.GlobalConfigModule
import io.ganguo.library.mvp.ui.mvp.integration.ConfigModule
import io.ganguo.library.mvp.util.CBaseUtils
import io.ganguo.library.mvp.util.GsonUtils
import io.realm.RealmList
import io.realm.RealmObject
import me.jessyan.retrofiturlmanager.RetrofitUrlManager
import java.io.IOException
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit

/**
 * ================================================
 * App 的全局配置信息在此配置, 需要将此实现类声明到 AndroidManifest 中
 * ConfigModule 的实现类可以有无数多个, 在 Application 中只是注册回调, 并不会影响性能 (多个 ConfigModule 在多 Module 环境下尤为受用)
 * ConfigModule 接口的实现类对象是通过反射生成的, 这里会有些性能损耗
 * ================================================
 */
class BaseAppConfiguration : ConfigModule {
    override fun priority(): Int = AppEnv.PRORITY_OF_BASE

    private val customExclusionStrategy = object : ExclusionStrategy{
        override fun shouldSkipField(f: FieldAttributes): Boolean {
            return f.declaringClass == RealmObject::class.java
        }

        override fun shouldSkipClass(clazz: Class<*>): Boolean {
            return false
        }
    }

    private val realmListInt = object : TypeToken<RealmList<RealmInt>>() {
    }.type
    private val realmListString = object : TypeToken<RealmList<RealmString>>() {
    }.type
    private val realmListDouble = object : TypeToken<RealmList<RealmDouble>>() {
    }.type

    private val realmListIntTypeAdapter = object : TypeAdapter<RealmList<RealmInt>>() {

        @Throws(IOException::class)
        override fun write(out: JsonWriter, value: RealmList<RealmInt>) {
            // Ignore
        }

        @Throws(IOException::class)
        override fun read(`in`: JsonReader): RealmList<RealmInt> {
            val list = RealmList<RealmInt>()
            `in`.beginArray()
            while (`in`.hasNext()) {
                list.add(RealmInt(`in`.nextInt()))
            }
            `in`.endArray()
            return list
        }
    }

    private val realmListStringTypeAdapter = object : TypeAdapter<RealmList<RealmString>>() {

        @Throws(IOException::class)
        override fun write(out: JsonWriter, value: RealmList<RealmString>) {
            // Ignore
        }

        @Throws(IOException::class)
        override fun read(`in`: JsonReader): RealmList<RealmString> {
            val list = RealmList<RealmString>()
            `in`.beginArray()
            while (`in`.hasNext()) {
                list.add(RealmString(`in`.nextString()))
            }
            `in`.endArray()
            return list
        }
    }

    private val realmListDoubleTypeAdapter = object : TypeAdapter<RealmList<RealmDouble>>() {

        @Throws(IOException::class)
        override fun write(out: JsonWriter, value: RealmList<RealmDouble>) {
            // Ignore
        }

        @Throws(IOException::class)
        override fun read(`in`: JsonReader): RealmList<RealmDouble> {
            val list = RealmList<RealmDouble>()
            `in`.beginArray()
            while (`in`.hasNext()) {
                list.add(RealmDouble(`in`.nextDouble()))
            }
            `in`.endArray()
            return list
        }
    }

    override fun applyOptions(context: Context, builder: GlobalConfigModule.Builder) {
        AppEnv.printEnv()

        if(CBaseUtils.isMainProcess(context)){
            val apiBaseUrl = AppEnv.API_BASE_URL + "/v1/"
            RetrofitUrlManager.getInstance().startAdvancedModel(apiBaseUrl)
            RetrofitUrlManager.getInstance().putDomain(AppConstants.API_BASE_URL, apiBaseUrl)

            builder
                .executorService(Executors.newCachedThreadPool())
                .baseurl(apiBaseUrl)
                .appDataPath(AppEnv.DATA_PATH)
                .printHttpLogLevel(if(AppEnv.DEV_MODE) RequestInterceptor.Level.ALL else RequestInterceptor.Level.NONE)
                .addInterceptor(AuthorizationInterceptor())
                .gsonConfiguration { _, gsonBuilder ->
                    gsonBuilder
                        .serializeNulls()//支持序列化值为 null 的参数
                        .setExclusionStrategies(customExclusionStrategy)
                        .registerTypeAdapter(realmListInt, realmListIntTypeAdapter)
                        .registerTypeAdapter(realmListString, realmListStringTypeAdapter)
                        .registerTypeAdapter(realmListDouble, realmListDoubleTypeAdapter)
                        .enableComplexMapKeySerialization()//支持将序列化 key 为 Object 的 Map, 默认只能序列化 key 为 String 的 Map
                }
                .retrofitConfiguration { _, retrofitBuilder ->
                    retrofitBuilder.addConverterFactory(APIResponseDataConverter.create(
                        GsonUtils.getGson(true),
                        null,
                        null
                    ))
                }
                .okhttpConfiguration { okhttpContext, okhttpBuilder ->
                    okhttpBuilder.writeTimeout(30, TimeUnit.SECONDS)
                    okhttpBuilder.connectTimeout(30, TimeUnit.SECONDS)
                    okhttpBuilder.readTimeout(30, TimeUnit.SECONDS)

                    if (AppEnv.DEV_MODE) {
                        okhttpBuilder.addInterceptor(ChuckInterceptor(okhttpContext))
                    }
                    if(AppEnv.openMockAPI){
                        okhttpBuilder.addInterceptor(MockAPIInterceptor())
                    }

                    //让 Retrofit 同时支持多个 BaseUrl 以及动态改变 BaseUrl. 详细使用请方法查看 https://github.com/JessYanCoding/RetrofitUrlManager
                    RetrofitUrlManager.getInstance().with(okhttpBuilder)
                }
                .rxCacheConfiguration { _, rxCacheBuilder ->
                    rxCacheBuilder.useExpiredDataIfLoaderNotAvailable(true)

                    null
                }
        }
    }

    override fun injectAppLifecycle(context: Context, lifecycles: MutableList<AppLifecycles>) {
        //AppLifecycles 中的所有方法都会在基类 Application 的对应生命周期中被调用, 所以在对应的方法中可以扩展一些自己需要的逻辑
        //可以根据不同的逻辑添加多个实现类
        lifecycles.add(BaseAppLifecyclesImpl())
    }

    override fun injectActivityLifecycle(context: Context, lifecycles: MutableList<Application.ActivityLifecycleCallbacks>) {
    }

    override fun injectFragmentLifecycle(context: Context, lifecycles: MutableList<FragmentManager.FragmentLifecycleCallbacks>) {}
}
